package tests.experiments.antstrat;

import jeconbond.automata.INode;
import jeconbond.automata.IOutcome;
import jeconbond.automata.Node;
import jeconbond.automata.Outcome;
import jeconbond.automata.behaviour.IWalkStrategy;
import jeconbond.automata.behaviour.antstrat.AntAgent;
import jeconbond.automata.behaviour.antstrat.FeramonStorrage;
import jeconbond.automata.behaviour.antstrat.IFeramonStorrage;
import jeconbond.economic.EconomicEngine;
import jeconbond.economic.IEconomicEngine;
import jeconbond.economic.strategy.IMerchant;
import jeconbond.economic.agent.AutomataAgent;
import jeconbond.economic.agent.IEconomicAgent;
import jeconbond.economic.agent.service.IPostActServiceAgent;
import jeconbond.economic.market.strategy.ObserveCostsProfitMerchantDecorator;
import jeconbond.economic.production.IResourceNecessityInformer;
import jeconbond.economic.production.IResourceNecessityInformer.InfoItem;
import jeconbond.economic.resources.IResourceType;
import jeconbond.economic.resources.ResourceBasket;
import jeconbond.economic.resources.ResourceStorrage;
import jeconbond.economic.resources.ResourceUtils;
import jeconbond.economic.systems.market.*;
import jeconbond.experiment.market.resourcerelations.BrosnenExperimentSettings;
import jeconbond.experiment.market.resourcerelations.generate.AntBasedAgentsCreator;
import jeconbond.experiment.market.resourcerelations.generate.BranchingProdAutomataGenerator;
import jeconbond.experiment.market.resourcerelations.generate.BrosnenEngineInitialiser;
import jeconbond.experiment.JEBExperimentFlow;
import jeconbond.experiment.market.generate.DefaultProfCostManagerFactory;
import jeconbond.experiment.generate.ProfitableNormFormGenerateDecorator;
import junit.framework.Assert;
import junit.framework.TestCase;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import laboratoryQ.computation.Random;
import laboratoryQ.computation.IRandom;
import laboratoryQ.flatness.Point;
import laboratoryQ.experiment.IExperiment;
import laboratoryQ.experiment.IExperimentResult;

public class TestAntBasedStrategies extends TestCase {
	public void testFeramonAdvisorInActingMarket() {
		IResourceType[] resources = ResourceUtils.generateResources("r", 8);
		INode root = genSmallExample(resources);
		IMarketResourceRedestributionSystem mrrs = 
			new DefaultMarketResourceRedestributionSystem();
		ResourceProvider resourceProvider = new ResourceProvider(
				mrrs,
				resources
		);
		IRandom random = new Random(12);
		ResourceStorrage inputRS = new ResourceStorrage();
		ResourceStorrage outputRS = new ResourceStorrage();
		IEconomicEngine engine = new EconomicEngine();
		engine.addAgents(resourceProvider);

		// MAIN
		IFeramonStorrage feramonStorrage = new FeramonStorrage(
				root, 
				5, 
				inputRS, 
				outputRS, 
				random,
				false
		);
		engine.addAgents(feramonStorrage.generateAntAgents(10, mrrs));
		// MAIN-end

		// ### ### ###
		engine.processSteps(100, mrrs);
		double pref0 = feramonStorrage.computeFeramon(root.getOutcomes().get(0));
		double pref1 = feramonStorrage.computeFeramon(root.getOutcomes().get(1));
		Assert.assertTrue(pref0 == pref1);

		// ### ### ###
		resourceProvider.makeResourcesCheap(
				0.1,
				resources[0], resources[1]
		);
		engine.processSteps(100, mrrs);
		pref0 = feramonStorrage.computeFeramon(root.getOutcomes().get(0));
		pref1 = feramonStorrage.computeFeramon(root.getOutcomes().get(1));
		Assert.assertTrue(pref0 > pref1);

		// ### ### ###
		resourceProvider.makeResourcesProfitable(
				100.0,
				resources[6], resources[7]
		);
		engine.processSteps(100, mrrs);
		pref0 = feramonStorrage.computeFeramon(root.getOutcomes().get(0));
		pref1 = feramonStorrage.computeFeramon(root.getOutcomes().get(1));
		Assert.assertTrue(pref0 < pref1);

		// ### ### ###
		resourceProvider.makeResourcesProfitable(
				1000.0,
				resources[2]
		);
		engine.processSteps(100, mrrs);
		pref0 = feramonStorrage.computeFeramon(root.getOutcomes().get(0));
		pref1 = feramonStorrage.computeFeramon(root.getOutcomes().get(1));
		Assert.assertTrue(pref0 > pref1);

		// ### ### ###
		resourceProvider.makeResourcesProfitable(
				10000.0,
				resources[6]
		);
		engine.processSteps(100, mrrs);
		pref0 = feramonStorrage.computeFeramon(root.getOutcomes().get(0));
		pref1 = feramonStorrage.computeFeramon(root.getOutcomes().get(1));
		Assert.assertTrue(pref0 < pref1);
	}

	public void testAntAgent() {
		IResourceType[] resources = ResourceUtils.generateResources("r", 8);
		INode root = genSmallExample(resources);
		IRandom random = new Random(12);
		ResourceStorrage inputRS = new ResourceStorrage();
		ResourceStorrage outputRS = new ResourceStorrage();
		IFeramonStorrage feramonStorrage = new FeramonStorrage(
				root, 
				5, 
				inputRS, 
				outputRS, 
				random 
		);
		IMarketResourceRedestributionSystem mrrs = new DefaultMarketResourceRedestributionSystem();
		AntAgent agent = (AntAgent) feramonStorrage.generateAntAgents(1, mrrs)[0];
		tests.fakes.FakeFeramonStorr faikFeramonStorrage = new tests.fakes.FakeFeramonStorr(feramonStorrage);
		agent.setFeramonStorrage(faikFeramonStorrage);

		ResourceProvider resourceProvider = new ResourceProvider(
				mrrs,
				resources
		);

		IEconomicEngine engine = new EconomicEngine();
		engine.addAgents(agent, resourceProvider);
		engine.processSteps(100, mrrs);

		Assert.assertEquals(96, faikFeramonStorrage.historyOut.size());
		INode n1 = root.getOutcomes().get(0).getDestination();
		INode n2 = root.getOutcomes().get(1).getDestination();
		int ch1 = 0;
		int ch2 = 0;
		for (IOutcome out : faikFeramonStorrage.historyOut) {
			if (out.getDestination() == n1) {
				ch1++;
			}
			if (out.getDestination() == n2) {
				ch2++;
			}
		}
		Assert.assertEquals(9, ch1);
		Assert.assertEquals(23, ch2);
	}

	public void testFeramonComputation() {
		int feramonHistoryLength = 3;
		IResourceType[] resources = ResourceUtils.generateResources("r", 8);
		INode root = genSmallExample(resources);
		ResourceStorrage inputRS = new ResourceStorrage();
		ResourceStorrage outputRS = new ResourceStorrage();
		IFeramonStorrage feramonStorrage = new FeramonStorrage(
				null, 
				feramonHistoryLength, 
				inputRS, 
				outputRS, 
				null, 
				false
		);
		double feramon;
		feramon = feramonStorrage.computeFeramon(
				root.getOutcomes().get(0)
		);
		Assert.assertEquals(1.0, feramon);
		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				2.0
		);
		feramon = feramonStorrage.computeFeramon(
				root.getOutcomes().get(0)
		);
		Assert.assertEquals(3.0, feramon);
		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				2.0
		);
		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				2.0
		);
		feramon = feramonStorrage.computeFeramon(
				root.getOutcomes().get(0)
		);
		Assert.assertEquals(4.5, feramon);

		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				4.0
		);
		feramon = feramonStorrage.computeFeramon(
				root.getOutcomes().get(0)
		);
		Assert.assertEquals(6.5, feramon);

		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				2.0
		);
		feramon = feramonStorrage.computeFeramon(
				root.getOutcomes().get(0)
		);
		Assert.assertEquals(5.5, feramon);
	}
	
	public void testStorrageAsWalkStrat() {
		int feramonHistoryLength = 5;
		IResourceType[] resources = ResourceUtils.generateResources("r", 8);
		INode root = genSmallExample(resources);
		ResourceStorrage inputRS = new ResourceStorrage();
		for (IResourceType rt : resources) {
			inputRS.storeResource(rt, 1e3);
		}
		ResourceStorrage outputRS = new ResourceStorrage();
		IFeramonStorrage feramonStorrage = new FeramonStorrage(
				null, 
				feramonHistoryLength, 
				inputRS, 
				outputRS, 
				null, 
				false
		);

		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				1.0
		);
		feramonStorrage.storeFeramon(
				root.getOutcomes().get(1),
				2.0
		);
		IWalkStrategy walkStrategy = feramonStorrage.getClientWalkStrategy();
		for (int i = 0; i < 10; i++) {
			setFeramonStorrageState(feramonStorrage, root);
			INode dest = walkStrategy.doWalk(root).getDestination();
			Assert.assertEquals("n2", dest.getName());
		}

		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				2.0
		);
		feramonStorrage.storeFeramon(
				root.getOutcomes().get(1),
				1.0
		);
		for (int i = 0; i < 10; i++) {
			setFeramonStorrageState(feramonStorrage, root);
			INode dest = walkStrategy.doWalk(root).getDestination();
			Assert.assertEquals("n1", dest.getName());
		}
	}

	static { System.out.println("\nBLOCKING AFTER_ANT_ALIVE feramon storrage choose 50 X 50 branching or feramon - basis"); }

	public void testNotWalkIfNotEnoughtResources() {
		int feramonHistoryLength = 5;
		IResourceType[] resources = ResourceUtils.generateResources("r", 8);
		INode root = genSmallExample(resources);
		ResourceStorrage inputRS = new ResourceStorrage();
		ResourceStorrage outputRS = new ResourceStorrage();
		IFeramonStorrage feramonStorrage = new FeramonStorrage(
				root,
				feramonHistoryLength,
				inputRS,
				outputRS,
				null,
				false
		);

		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				1e5
		);
		feramonStorrage.storeFeramon(
				root.getOutcomes().get(1),
				0.0
		);
		IResourceNecessityInformer resNecInf = feramonStorrage.getClientResourceNecessityInformer();
		List<InfoItem> necResItems = resNecInf.listInfoItems();
		Assert.assertEquals(1, necResItems.size());
		IResourceType type = necResItems.get(0).basket.type;
		Assert.assertEquals("r0", type.toString());

		IOutcome walked = feramonStorrage.getClientWalkStrategy().doWalk(root);
		Assert.assertNull(walked);
	}

	public void testStorrageAsResourceNecInformer() {
		int feramonHistoryLength = 5;
		IResourceType[] resources = ResourceUtils.generateResources("r", 8);
		INode root = genSmallExample(resources);
		ResourceStorrage inputRS = new ResourceStorrage();
		ResourceStorrage outputRS = new ResourceStorrage();
		IFeramonStorrage feramonStorrage = new FeramonStorrage(
				root,
				feramonHistoryLength,
				inputRS,
				outputRS,
				null,
				false
		);

		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				1e5
		);
		feramonStorrage.storeFeramon(
				root.getOutcomes().get(1),
				0.0
		);
		IResourceNecessityInformer resNecInf = feramonStorrage.getClientResourceNecessityInformer();
		List<InfoItem> necResItems = resNecInf.listInfoItems();
		Assert.assertEquals(1, necResItems.size());
		IResourceType type = necResItems.get(0).basket.type;
		Assert.assertEquals("r0", type.toString());

		// ### ### ###
		feramonStorrage = new FeramonStorrage(
				root,
				feramonHistoryLength,
				inputRS,
				outputRS,
				null,
				false
		);

		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				0.0
		);
		feramonStorrage.storeFeramon(
				root.getOutcomes().get(1),
				1e5
		);
		resNecInf = feramonStorrage.getClientResourceNecessityInformer();
		necResItems = resNecInf.listInfoItems();
		Assert.assertEquals(1, necResItems.size());
		type = necResItems.get(0).basket.type;
		Assert.assertEquals("r3", type.toString());
	}
	
	public void testFullAntWorld() {
		JEBExperimentFlow experimentFlow = new JEBExperimentFlow();
		
		BrosnenExperimentSettings settings = 
			new BrosnenExperimentSettings();
		settings.initialise();
		
		settings.stepSize = 10;
		settings.stepCount = 10;
		settings.agentsCount = 10;
		settings.initMoney = 50.0;
		settings.initResourceExceedPercent = 1.1;
		
		settings.chainLength = 4;
		settings.branchesCount = 3;
		settings.postBranchCount = 1;
		
		settings.agentGreedPercent = 1.5;
		settings.produceProfit = 1.3;
		settings.concurrentPricePercent = 0.3;
		settings.observeCostsTraceLength = 5;
		
		settings.rrsFactory = new DefaultMarketRRSFactory();
		settings.resources = ResourceUtils.generateResources("r", 3);
		settings.experiment = experimentFlow; 
		settings.randomSeed = 0.0; 
		settings.zeroPoint = new Point(0.0, 0.0);
		
		settings.purchaseExceedDayBudget = -2.0;
		settings.purchaseExceedPassivePeriod = -2;
		settings.purchaseExceedUseTrackLength = -2;
		settings.utiliseExceedPeriod = -2;
		settings.selectPropositionsPart = -1.0;
		settings.feramonHistoryLength = 5;
		settings.antAgentCount = 3;
		settings.profitCostManagerFactory = new DefaultProfCostManagerFactory();
		settings.setCommoditiesDependGroups(null);
		settings.setCommoditiesDependGroupsPower(-2.0);
		settings.goalResourceIndex = -2;
		settings.timeStepX = -2;
		settings.speculantPropCountMarketPart = 0.0;
		settings.speculantGoalValueMarketPart = 0.0;
		settings.validate();

		experimentFlow.automataGenerator =
				new ProfitableNormFormGenerateDecorator(new BranchingProdAutomataGenerator());
		experimentFlow.agentCreator = new AntBasedAgentsCreator();
		experimentFlow.engineInitialiser = new BrosnenEngineInitialiser();
	
		// ### ### ### ###
		IExperimentResult expres = experimentFlow.runExperiment(settings, IExperiment.NOPExpRunStatusHandler.INSTANCE);
		Assert.assertNotNull(expres);
	}

	public void testStorrageAsAntWalkStrategy_WITH_branchingBasedEuristic() {
		IRandom random = new Random(1234);
		int feramonHistoryLength = 3;
		IResourceType[] resources = ResourceUtils.generateResources("r", 8);
		INode root = genSmallExample(resources);
		addBranches(root, 5);
		ResourceStorrage inputRS = new ResourceStorrage();
		ResourceStorrage outputRS = new ResourceStorrage();
		IFeramonStorrage feramonStorrage = new FeramonStorrage(
				root, 
				feramonHistoryLength, 
				inputRS, 
				outputRS, 
				random
		);
		IMarketResourceRedestributionSystem mrrs = new DefaultMarketResourceRedestributionSystem();
		IWalkStrategy walkStrategy = generateAntWalkStrategy(feramonStorrage, mrrs);

		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				1.0
		);
		feramonStorrage.storeFeramon(
				root.getOutcomes().get(1),
				1.0
		);
		int ch0 = 0;
		int ch1 = 0;
		for (int i = 0; i < 1000; i++) {
			INode dest = callAntChoose(walkStrategy, root);
			if (dest == root.getOutcomes().get(0).getDestination()) {
				ch0++;
			}
			if (dest == root.getOutcomes().get(1).getDestination()) {
				ch1++;
			}
		}
		Assert.assertEquals(117, ch0);
		Assert.assertEquals(883, ch1);

		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				20.0
		);
		ch0 = 0;
		ch1 = 0;
		for (int i = 0; i < 1000; i++) {
			INode dest = callAntChoose(walkStrategy, root);
			if (dest == root.getOutcomes().get(0).getDestination()) {
				ch0++;
			}
			if (dest == root.getOutcomes().get(1).getDestination()) {
				ch1++;
			}
		}
		Assert.assertEquals(580, ch0);
		Assert.assertEquals(420, ch1);
	
		addBranches(root, 20);
		feramonStorrage = new FeramonStorrage(
				root, 
				feramonHistoryLength, 
				inputRS, 
				outputRS, 
				random 
		);
		walkStrategy = generateAntWalkStrategy(feramonStorrage, mrrs);
		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				1.0
		);
		feramonStorrage.storeFeramon(
				root.getOutcomes().get(1),
				1.0
		);
		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				20.0
		);
		ch0 = 0;
		ch1 = 0;
		for (int i = 0; i < 1000; i++) {
			INode dest = callAntChoose(walkStrategy, root);
			if (dest == root.getOutcomes().get(0).getDestination()) {
				ch0++;
			}
			if (dest == root.getOutcomes().get(1).getDestination()) {
				ch1++;
			}
		}
		Assert.assertEquals(284, ch0);
		Assert.assertEquals(716, ch1);
	}

	public void testStorrageAsAntWalkStrategy() {
		IRandom random = new Random(1234);
		int feramonHistoryLength = 3;
		IResourceType[] resources = ResourceUtils.generateResources("r", 8);
		INode root = genSmallExample(resources);
		ResourceStorrage inputRS = new ResourceStorrage();
		ResourceStorrage outputRS = new ResourceStorrage();
		IFeramonStorrage feramonStorrage = new FeramonStorrage(
				null, 
				feramonHistoryLength, 
				inputRS, 
				outputRS, 
				random, 
				false
		);
		IMarketResourceRedestributionSystem mrrs = new DefaultMarketResourceRedestributionSystem();
		IWalkStrategy walkStrategy = generateAntWalkStrategy(feramonStorrage, mrrs);

		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				2.0
		);
		feramonStorrage.storeFeramon(
				root.getOutcomes().get(1),
				1.0
		);
		int ch0 = 0;
		int ch1 = 0;
		for (int i = 0; i < 300; i++) {
			INode dest = callAntChoose(walkStrategy, root);
			if (dest == root.getOutcomes().get(0).getDestination()) {
				ch0++;
			}
			if (dest == root.getOutcomes().get(1).getDestination()) {
				ch1++;
			}
		}
		Assert.assertEquals(181, ch0);
		Assert.assertEquals(119, ch1);

		feramonStorrage.storeFeramon(
				root.getOutcomes().get(0),
				1.0
		);
		feramonStorrage.storeFeramon(
				root.getOutcomes().get(1),
				5.0
		);
		ch0 = 0;
		ch1 = 0;
		for (int i = 0; i < 820; i++) {
			INode dest = callAntChoose(walkStrategy, root);
			if (dest == root.getOutcomes().get(0).getDestination()) {
				ch0++;
			}
			if (dest == root.getOutcomes().get(1).getDestination()) {
				ch1++;
			}
		}
		Assert.assertEquals(268, ch0);
		Assert.assertEquals(552, ch1);
	}
	
	public void testFakeAntPurchase() {
		IResourceType[] resources = ResourceUtils.generateResources("r", 8);
		INode root = genSmallExample(resources);
		ResourceStorrage inputRS = new ResourceStorrage();
		ResourceStorrage outputRS = new ResourceStorrage();
		IRandom random = new Random();
		final IMarketResourceRedestributionSystem mrrs = new DefaultMarketResourceRedestributionSystem();
		
		IFeramonStorrage storrage = new FeramonStorrage(
				root, 
				5, 
				inputRS,
				outputRS,
				random 
		);
		
		IEconomicAgent antagent = 
			storrage.generateAntAgents(1, mrrs)[0];
		
		ResourceStorrage provStorrage = new ResourceStorrage();
		IMoneyAccount provMoneyAccount = new MoneyAccount();
		final List<ISellProposition> props = new ArrayList<ISellProposition>();
		for (IResourceType rt : resources) {
			provStorrage.storeResource(rt, 1e5);
			ISellProposition sp = new SellProposition(
					rt, 
					1.0,
					provStorrage, 
					provMoneyAccount
			);
			props.add(sp);
		}
		
		IEconomicEngine engine = new EconomicEngine();
		engine.addAgents(antagent);
		engine.addAgents(
				new tests.fakes.ReRegistePropositionsAgent(props, mrrs)
		);
		double initFeramon = storrage.computeFeramon(root.getOutcomes().get(0));
		engine.processSteps(100, mrrs);

		Assert.assertTrue(storrage.computeFeramon(root.getOutcomes().get(0)) > initFeramon);
		for (IResourceType rt : resources) {
			Assert.assertEquals(1e5, provStorrage.howMuchWasStored(rt));
		}
		Assert.assertEquals(0.0, provMoneyAccount.getBalance());
	}
	
	public void testFeramonStorrStateChange() {
		IResourceType[] resources = ResourceUtils.generateResources("r", 8);
		INode root = genSmallExample(resources);
		IRandom random = new Random();
		IMarketResourceRedestributionSystem mrrs = new DefaultMarketResourceRedestributionSystem();
		
		AutomataAgent automataAgent = new AutomataAgent(root);
		IFeramonStorrage storrage = new FeramonStorrage(
				root, 
				5, 
				automataAgent.getInputRS(),
				automataAgent.getOutputRS(),
				random 
		);
		
		IEconomicAgent antagent = 
			storrage.generateAntAgents(1, mrrs)[0];
		
		ResourceStorrage provStorrage = new ResourceStorrage();
		IMoneyAccount provMoneyAccount = new MoneyAccount();
		final List<ISellProposition> props = new ArrayList<ISellProposition>();
		for (IResourceType rt : resources) {
			provStorrage.storeResource(rt, 1e5);
			ISellProposition sp = new SellProposition(
					rt, 
					1.0,
					provStorrage, 
					provMoneyAccount
			);
			props.add(sp);
		}
		
		IEconomicEngine engine = new EconomicEngine();
		engine.addAgents(antagent);
		engine.addAgents(
				new tests.fakes.ReRegistePropositionsAgent(props, mrrs)
		);
		automataAgent.setWalkStrategy(storrage.getClientWalkStrategy());
		IMoneyAccount agentMoney = new MoneyAccount(1e5);
		ObserveCostsProfitMerchantDecorator observeCPMerch = new ObserveCostsProfitMerchantDecorator(
				automataAgent,
				storrage.getClientResourceNecessityInformer(),
				mrrs,
				agentMoney,
				1,
				1.0
		);
		automataAgent.setMerchant(observeCPMerch);
		engine.addAgents(automataAgent);
		engine.addAgents(observeCPMerch);

		Assert.assertEquals("root", automataAgent.getState().getName());
		engine.processSteps(3, mrrs);
		Assert.assertEquals("n11", automataAgent.getState().getName());
		Assert.assertEquals("n11", getFeramonState(storrage).getName());
	}

	private void setFeramonStorrageState(
			IFeramonStorrage feramonStorrage,
			INode state
	) {
		try {
			Class<?> clazz = feramonStorrage.getClass();
			Field field = clazz.getDeclaredField("state");
			field.setAccessible(true);
			field.set(feramonStorrage, state);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	private INode getFeramonState(IFeramonStorrage feramonStorrage) {
		try {
			Class<?> clazz = feramonStorrage.getClass();
			Field field = clazz.getDeclaredField("state");
			field.setAccessible(true);
			return (INode) field.get(feramonStorrage);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private IWalkStrategy generateAntWalkStrategy(
			IFeramonStorrage feramonStorrage,
			IMarketResourceRedestributionSystem mrrs
	) {
		try {
			AntAgent antAgent = (AntAgent) feramonStorrage.generateAntAgents(1, mrrs)[0];
			ObserveCostsProfitMerchantDecorator ocMerchant =
					(ObserveCostsProfitMerchantDecorator) antAgent.getMerchant();
			IMerchant merchant = ocMerchant.getSubmerchant();
			Class<?> clazz = merchant.getClass();
			Field field = clazz.getDeclaredField("informer");
			field.setAccessible(true);
			return (IWalkStrategy) field.get(merchant);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private INode callAntChoose(IWalkStrategy walkStrategy, INode root) {
		Class<? extends IWalkStrategy> clazz = walkStrategy.getClass();
		try {
			Method choose = clazz.getDeclaredMethod("chooseOutcome", INode.class);
			choose.setAccessible(true);
			choose.invoke(walkStrategy, root);
			Field field = clazz.getDeclaredField("choosenOut");
			field.setAccessible(true);
			Object res = field.get(walkStrategy);
			return ((IOutcome) res).getDestination();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static class ResourceProvider implements IPostActServiceAgent {
		private IMarketResourceRedestributionSystem mrrs;
		private IResourceType[] resources;
		private IMoneyAccount money = new MoneyAccount(Double.MAX_VALUE);
		private ISellProposition[] propositions;
		private Map<IResourceType, Double> resource2price = 
			new HashMap<IResourceType, Double>();
		private ResourceStorrage storrage = new ResourceStorrage();
		
		public ResourceProvider(
				IMarketResourceRedestributionSystem mrrs,
				IResourceType[] resources
		) {
			this.mrrs = mrrs;
			this.resources = resources;
			propositions = new ISellProposition[resources.length];
			
			initialiseResource2price();
			initialisePropositions();
		}

		public void makeResourcesCheap(
				double value,
				IResourceType ... cheapRess
		) {
			for (IResourceType rt : cheapRess) {
				resource2price.put(rt, value);
			}
			initialisePropositions();
		}

		public void makeResourcesProfitable(
				double value,
				IResourceType ... profRess
		) {
			for (IResourceType rt : profRess) {
				resource2price.put(rt, value);
			}
			initialisePropositions();
		}

		private void initialisePropositions() {
			storrage.clear();
			for (int i = 0; i < resources.length; i++) {
				propositions[i] = new  SellProposition(
						resources[i],
						resource2price.get(resources[i]),
						storrage,
						money
				);
				storrage.storeResource(resources[i], 1e5);
			}
		}
		
		private void initialiseResource2price() {
			for (IResourceType rt : resources) {
				resource2price.put(rt, 1.0);
			}
		}

		@Override
		public void processPreProduceStage() {
		}
		
		@Override
		public void processProduceStage() {
		}
		
		@Override
		public void processPostProduceStage() {
			for (ISellProposition prop : propositions) {
				mrrs.registerProposition(prop);
			}
		}
	}


	private void addBranches(INode root, int count) {
		INode br = root.getOutcomes().get(1).getDestination();
		for (int i = 0; i < count; i++) {
			INode addit = new Node("n2" + (br.getOutcomes().size() + 1));
			IOutcome out = new Outcome(addit);
			br.getOutcomes().add(out);
			addit.getOutcomes().add(new Outcome(root));
		}
	}

	public static INode genSmallExample(IResourceType[] resources) {
		INode root = new Node("root");
		
		IOutcome out;
		
		INode n1 = new Node("n1");
		out = new Outcome(n1, new ResourceBasket(resources[0], 1.0));
		root.getOutcomes().add(out);
		INode n11 = new Node("n11");
		out = new Outcome(n11, new ResourceBasket(resources[1], 1.0));
		n1.getOutcomes().add(out);
		out = new Outcome(root);
		out.setProducedResources(new ResourceBasket(resources[2], 4.0));
		n11.getOutcomes().add(out);
		
		INode n2 = new Node("n2");
		out = new Outcome(n2, new ResourceBasket(resources[3], 1.0));
		root.getOutcomes().add(out);
		INode n21 = new Node("n21");
		out = new Outcome(n21, new ResourceBasket(resources[4], 1.0));
		n2.getOutcomes().add(out);
		INode n22 = new Node("n22");
		out = new Outcome(n22, new ResourceBasket(resources[5], 1.0));
		n2.getOutcomes().add(out);
		
		out = new Outcome(root);
		out.setProducedResources(new ResourceBasket(resources[6], 4.0));
		n21.getOutcomes().add(out);
		out = new Outcome(root);
		out.setProducedResources(new ResourceBasket(resources[7], 4.0));
		n22.getOutcomes().add(out);
		return root;
	}
}
